
// INCLUDES ********************************************************************

#include "Vector2D.h"

#include "Sprite.h"

#include "ES1Renderer.h"

#include "ES2Renderer.h"

#include "TouchController.h"

#include "TextureCache.h"

#include "Configuration.h"

#import "EAGLView.h"

#include "GameController.h"



static const float gMinGL20Version = 3.0f;

// NAMESPACES ******************************************************************

// FORWARD DECLARATIONS ********************************************************

//! x and y values need to be swapped when in landscape mode
void swap( float* x, float* y);

// *****************************************************************************

#pragma mark -
#pragma mark Helper functions
// -----------------------------------------------------------------------------

void swap( float* x, float* y)
{
    float tmp = *x;
    
    *x = *y;
    
    *y = tmp;
}

#pragma mark -
#pragma mark Private methods
// -----------------------------------------------------------------------------

@interface EAGLView ()

- ( void)setupGLViewForOrientation:( UIInterfaceOrientation) orientation;

- ( void)update;

@end


#pragma mark -
#pragma mark Implementation
// -----------------------------------------------------------------------------


@implementation EAGLView

// -----------------------------------------------------------------------------

+ ( Class) layerClass
{
    return [CAEAGLLayer class];
}

#pragma mark -
#pragma mark Setup
// -----------------------------------------------------------------------------

- ( id)initWithFrame:( CGRect) frame orientation:( UIInterfaceOrientation) orientation andOptions:( NSDictionary*) options
{
    self = [super initWithFrame:frame];

    if ( !self)
    {
        return nil;
    }
    
    NSDictionary* properties = nil;

    if ( options)
    {
        properties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:FALSE],
                                        kEAGLDrawablePropertyRetainedBacking, 
                                        kEAGLColorFormatRGBA8,
                                        kEAGLDrawablePropertyColorFormat, 
                                        nil
                                        ];
    }
    else
    {
        properties = options;
    }
    
    CAEAGLLayer* eaglLayer = ( CAEAGLLayer*) self.layer;
    
    eaglLayer.drawableProperties = properties;
    
    const float OSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
    
    //! if 3.0 or higher, we can use OpenGL ES 2.0
    if ( OSVersion >= gMinGL20Version)
    {
        /// @todo: insert 2.0 renderer here
        mRenderer = [[ES1Renderer alloc] initWithCALayer:eaglLayer];
    }
    else
    {
        mRenderer = [[ES1Renderer alloc] initWithCALayer:eaglLayer];
    }
    
    BOOL frameBuffersCreated = [mRenderer createFrameBuffers];
    
    if ( frameBuffersCreated == NO || !mRenderer)
    {
        [self release];
        
        return nil;
    }
    
    mAccelerationDelegate = [[AccelerationDelegate alloc] init];
    
    [[UIApplication sharedApplication] setStatusBarOrientation:orientation];
    
    //! we need a valid context to check for extensions
    const char* glExtensions = ( char*) glGetString( GL_EXTENSIONS); 
    
    if ( strstr( glExtensions, "GL_APPLE_texture_2D_limited_npot") != 0)
    {
        Meteor::Configuration::Instance().SetValueForKey<bool>( "NPOT support", true);
    }
    
    return self;
}

// -----------------------------------------------------------------------------

- ( void)adaptOrientation:( UIInterfaceOrientation) orientation
{
    const Meteor::Configuration& config = Meteor::Configuration::Instance();

    Meteor::Vector2D screenSize = config.ValueForKey<Meteor::Vector2D>("Screensize");
    
    mTouchOrientationCompensationY = screenSize.Y();
    
    [self setupGLViewForOrientation:orientation];
}

// -----------------------------------------------------------------------------

- ( void)setupGLViewForOrientation:( UIInterfaceOrientation) orientation
{
    CGRect screenBounds = [[UIScreen mainScreen] bounds];

    float width = screenBounds.size.width;
    
    float height = screenBounds.size.height;
    
    glMatrixMode( GL_PROJECTION);

    glLoadIdentity();
    
    glViewport( 0, 0, width, height);
    
    if ( orientation == UIInterfaceOrientationLandscapeLeft ||
         orientation == UIInterfaceOrientationLandscapeRight)
    {
        swap( &width, &height);
    }
    
    glRotatef( 0, 0, 0, 1);
    
    glOrthof( 0.0f,
              width, 
              0.0f, 
              height,
              -10000.0f, 
              10000.0f);

    glMatrixMode( GL_MODELVIEW);
    
    glLoadIdentity();
    
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
}


#pragma mark -
#pragma mark Start/stop rendering
// -----------------------------------------------------------------------------

- ( void)startRendering
{
    if ( mRendering == true)
    {
        return;
    }
    
    if ( Meteor::Configuration::Instance().ValueForKey<bool>( "DisplayLink support"))
    {
        Class displayLink = NSClassFromString( @"CADisplayLink");
        
        mDisplayLink = [displayLink displayLinkWithTarget:self 
                                                      selector:@selector( update)
                        ];
        
        [mDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] 
                           forMode:NSDefaultRunLoopMode
         ];
    }
    else
    {
        mRenderTimer = [NSTimer scheduledTimerWithTimeInterval:( 1.0f / 60.0f) 
                                                        target:self 
                                                      selector:@selector( update) 
                                                      userInfo:nil 
                                                       repeats:YES];
    }
    
    mRendering = YES;
}

// -----------------------------------------------------------------------------

- ( void)stopRendering
{
	if ( mRendering == true)
	{
		if ( Meteor::Configuration::Instance().ValueForKey<bool>( "DisplayLink support"))
		{
			[mDisplayLink invalidate];
            
            mDisplayLink = nil;
		}
		else
		{
			[mRenderTimer invalidate];
            
			mRenderTimer = nil;
		}
        
		mRendering = NO;
	}
}

#pragma mark -
#pragma mark Touch handling
// -----------------------------------------------------------------------------

- ( void)touchesBegan:( NSSet*) touches withEvent:( UIEvent*) event
{
    UITouch* touch = [[touches allObjects] objectAtIndex:0];
    
    CGPoint touchPos = [touch locationInView:self];
    
    float x = touchPos.x;
    
    float y = abs( touchPos.y-mTouchOrientationCompensationY);
    
    Meteor::TouchController::Instance().TouchBegan( Meteor::Vector2D( x, y));
}

// -----------------------------------------------------------------------------

- ( void)touchesMoved:( NSSet*) touches withEvent:( UIEvent*) event
{
    UITouch* touch = [[touches allObjects] objectAtIndex:0];
    
    CGPoint touchPos = [touch locationInView:self];
    
    float x = touchPos.x;
    
    float y = abs( touchPos.y-mTouchOrientationCompensationY);
    
    Meteor::TouchController::Instance().TouchMoved( Meteor::Vector2D( x, y));
}

// -----------------------------------------------------------------------------

- ( void)touchesEnded:( NSSet*) touches withEvent:( UIEvent*) event
{
    UITouch* touch = [[touches allObjects] objectAtIndex:0];
    
    CGPoint touchPos = [touch locationInView:self];
    
    float x = touchPos.x;
    
    float y = abs( touchPos.y-mTouchOrientationCompensationY);

    Meteor::TouchController::Instance().TouchEnded( Meteor::Vector2D( x, y));
}

// -----------------------------------------------------------------------------

- (void)touchesCancelled:( NSSet*) touches withEvent:( UIEvent*) event
{
}

#pragma mark -
#pragma mark Periodic updates
// -----------------------------------------------------------------------------

- ( void)update
{
    // everthing gets updated from the controller
    Meteor::GameController::Instance().Update();
    
    Meteor::GameController::Instance().Draw();
    
    [mRenderer rendersBuffers];
}


#pragma mark -
#pragma mark Deallocation
// -----------------------------------------------------------------------------

- ( void)dealloc
{
    [mRenderer release];
    
    [mAccelerationDelegate release];

    [super dealloc];
}

@end
